//go:build go1.18

package godb

import (
	"errors"
	"gitee.com/kinwyb/conv"
	"reflect"
	"strings"
)

// QueryResultToStructArray 结果转换成结构体数组
func QueryResultToStructArray[T any](res *QueryResult) ([]T, error) {
	var ts T
	if reflect.TypeOf(ts).Kind() != reflect.Struct {
		return nil, errors.New("只能转换内容到结构体")
	}
	length := res.Length()
	if length < 1 {
		return nil, nil
	}
	var ret = make([]T, length)
	row := map[string]any{}
	columns := res.Columns()
	datas := res.Rows()
	for i := 0; i < length; i++ {
		data := datas[i]
		for i, vv := range columns {
			row[vv] = data[i]
		}
		var r T
		SetValueToStruct(&r, row)
		ret[i] = r
	}
	return ret, nil
}

// QueryResultToStructPtrArray 结果转换成结构体指针数组
func QueryResultToStructPtrArray[T any](res *QueryResult) ([]*T, error) {
	var ts T
	if reflect.TypeOf(ts).Kind() != reflect.Struct {
		return nil, errors.New("只能转换内容到结构体")
	}
	length := res.Length()
	if length < 1 {
		return nil, nil
	}
	var ret = make([]*T, length)
	row := map[string]any{}
	columns := res.Columns()
	datas := res.Rows()
	for i := 0; i < length; i++ {
		data := datas[i]
		for i, vv := range columns {
			row[vv] = data[i]
		}
		var r T
		SetValueToStruct(&r, row)
		ret[i] = &r
	}
	return ret, nil
}

// QueryResultFieldToBaseTypeArray 结果某个字段转换成数组
func QueryResultFieldToBaseTypeArray[T any](res *QueryResult, fieldName string) ([]T, error) {
	var ts T
	d := reflect.ValueOf(ts)
	dType := d.Type().String()
	switch dType {
	case "string", "int8", "int16", "int32", "int", "int64", "float32", "float64", "bool":
	default:
		return nil, errors.New("该方法只支持基础数据类型")
	}
	columns := res.Columns()
	columnIndex := -1
	for i, column := range columns {
		if column == fieldName {
			columnIndex = i
		}
	}
	if columnIndex < 0 || columnIndex > (len(columns)-1) {
		return nil, errors.New("结果不存在指定字段")
	}
	d = reflect.ValueOf(&ts)
	valueOfTs := d.Elem()
	datas := res.Rows()
	var ret = make([]T, len(datas))
	for i, data := range datas {
		val := data[columnIndex]
		switch dType {
		case "string":
			valueOfTs.SetString(strings.TrimSpace(conv.ToString(val)))
		case "int8", "int16", "int32", "int", "int64":
			valueOfTs.SetInt(conv.ToInt64(val))
		case "float32", "float64":
			valueOfTs.SetFloat(conv.ToFloat64(val))
		case "bool":
			valueOfTs.SetBool(conv.ToBool(val))
		}
		ret[i] = ts
	}
	return ret, nil
}

// QueryResultRowToStruct 结果转换成结构体
func QueryResultRowToStruct[T any](res *QueryResult, rowIndex ...int) (T, error) {
	var ts T
	if reflect.TypeOf(ts).Kind() != reflect.Struct {
		return ts, errors.New("只能转换内容到结构体")
	}
	length := res.Length()
	if length < 1 {
		return ts, nil
	}
	index := 0
	if len(rowIndex) > 0 {
		index = rowIndex[0]
	}
	if index < 0 || index > length-1 {
		return ts, errors.New("错误的索引值")
	}
	row := map[string]any{}
	columns := res.Columns()
	data := res.Rows()[index]
	for i, vv := range columns {
		row[vv] = data[i]
	}
	var r T
	SetValueToStruct(&r, row)
	return r, nil
}

// QueryResultRowToStructPtr 结果转换成结构体指针
func QueryResultRowToStructPtr[T any](res *QueryResult, rowIndex ...int) (*T, error) {
	ret, err := QueryResultRowToStruct[T](res, rowIndex...)
	if err != nil {
		return nil, err
	}
	return &ret, nil
}

// QueryResultRowFieldToBaseType 结果某个字段转换成
func QueryResultRowFieldToBaseType[T any](res *QueryResult, fieldName string, rowIndex ...int) (T, error) {
	var ts T
	d := reflect.ValueOf(ts)
	dType := d.Type().String()
	switch dType {
	case "string", "int8", "int16", "int32", "int", "int64", "float32", "float64", "bool":
	default:
		return ts, errors.New("该方法只支持基础数据类型")
	}
	columns := res.Columns()
	columnIndex := -1
	for i, column := range columns {
		if column == fieldName {
			columnIndex = i
		}
	}
	if columnIndex < 0 || columnIndex > (len(columns)-1) {
		return ts, errors.New("结果不存在指定字段")
	}
	index := 0
	if len(rowIndex) > 0 {
		index = rowIndex[0]
	}
	if index < 0 || index > res.Length()-1 {
		return ts, errors.New("错误的索引值")
	}
	d = reflect.ValueOf(&ts)
	valueOfTs := d.Elem()
	data := res.Rows()[index]
	val := data[columnIndex]
	switch dType {
	case "string":
		valueOfTs.SetString(strings.TrimSpace(conv.ToString(val)))
	case "int8", "int16", "int32", "int", "int64":
		valueOfTs.SetInt(conv.ToInt64(val))
	case "float32", "float64":
		valueOfTs.SetFloat(conv.ToFloat64(val))
	case "bool":
		valueOfTs.SetBool(conv.ToBool(val))
	}
	return ts, nil
}
